import yaml
import sys
from .config import *
from .logger import *
from .network import *
from .env import *
from .downloaders import *
import besins_selenium_downloaders.streamlit_app.app
import os
import inspect
import subprocess
from .decorator import log_function_call
import argparse
import socket


@log_function_call
def perform_initial_setup():
    env_path = get_env_file_path()
    config_path = get_config_file_path()

    chrome_driver_path = (
        load_configuration().get("selenium", {}).get("chrome_driver_path")
    )

    firefox_driver_path = (
        load_configuration().get("selenium", {}).get("firefox_driver_path")
    )

    if not os.path.isfile(env_path):
        logger.error(
            f"❌ {env_path} is missing. Please create it with your environment variables."
        )
    else:
        logger.info(f"✅ {env_path} is present.")

    if not os.path.isfile(config_path):
        logger.error(
            f"❌ {config_path} is missing. Please create it with your configuration settings."
        )
    else:
        logger.info(f"✅ {config_path} is present.")

    # Check for ChromeDriver
    if os.path.exists(chrome_driver_path):
        logger.info(f"✅ ChromeDriver is found at {chrome_driver_path}.")
    else:
        logger.error("❌ ChromeDriver is not found.")
        logger.info(
            "This might be due to the Great Firewall in China blocking ChromeDriverManager().install()."
        )
        logger.info("ChromeDriver installation is required only once.")
        install_chrome_driver()
        logger.info(
            "✅ ChromeDriver installed successfully. You can proceed without VPN."
        )

    # Check for FirefoxDriver
    if os.path.exists(firefox_driver_path):
        logger.info(f"✅ FirefoxDriver is found at {firefox_driver_path}.")
    else:
        logger.error("❌ FirefoxDriver is not found.")
        logger.info("This might be due to the Great Firewall in China blocking FirefoxDriverManager().install().")
        logger.info("FirefoxDriver installation is required only once.")
        install_firefox_driver()

    config = load_configuration()

    # Check for 2Captcha API key
    two_captcha_key = is_2captcha_api_key_present()
    if not two_captcha_key:
        logger.error("❌ 2Captcha API key is missing in the .env configuration.")
    else:
        logger.info("✅ 2Captcha API key is present in the .env configuration.")

    # Check for Excel file
    excel_file = config.get("secret_files", {}).get("xlsx")
    if not excel_file:
        logger.error("❌ Excel file path is missing in the configuration.")
    else:
        logger.info("✅ Excel file path is present in the configuration.")

    # Check if the Excel file exists
    if not os.path.isfile(excel_file):
        logger.error(f"❌ Excel file does not exist: {excel_file}")
    else:
        logger.info(f"✅ Excel file exists: {excel_file}")

    # Validate that there are no conflicts in site names before proceeding
    try:
        validate_site_name_conflicts()
        logger.info("✅ Site name conflicts validated successfully.")
    except ValueError as e:
        logger.error(f"❌ Site name conflict detected: {e}")
        sys.exit(1)  # Exit the program if there are conflicts


@log_function_call
def run_one_website(site: str):
    websites = get_website_names()
    websites_lower = [website.lower() for website in websites]

    if site.lower() not in websites_lower:
        logger.error(
            f"Error: '{site}' is not a valid website in the configuration. Valid options are: \n [{', '.join(websites)}]."
        )
        return

    index = websites_lower.index(site.lower())
    class_name = websites[index]
    downloader_class = globals().get(class_name)

    if downloader_class is None:
        logger.error(f"Error: Downloader class '{class_name}' not found.")
        return

    downloader_instance = downloader_class()
    downloader_instance.main()
    return downloader_instance.whole_process_success, downloader_instance

@log_function_call
def besins_run_one_website():
    if len(sys.argv) == 2:
        site_name = sys.argv[1]
        run_one_website(site_name)
    else:
        websites = get_sorted_website_names()
        logger.error(
            f"Usage: besins_run_one_website [site_name]. Valid site options are: \n[{', '.join(websites)}]."
        )

@log_function_call
def run_all_websites():
    if validate_site_name_conflicts():
        for site_name in get_website_names():
            downloader_class = globals().get(site_name)
        if downloader_class is None:
            logger.error(f"Error: Downloader class '{site_name}' not found.")
            return
        else:
            downloader_instance = downloader_class()
            try:
                downloader_instance.main()
            except Exception as e:
                logger.error(
                    f"An error occurred while running the downloader for {site_name}: {e}"
                )
    else:
        logger.error("❌ Site name conflicts detected. Please check config.yml.")
        return


@log_function_call
def besins_run_all_websites():
    run_all_websites()


@log_function_call
def run_multiple_websites(sites: list[str]):
    websites = get_website_names()
    websites_lower = [website.lower() for website in websites]
    for site in sites:
        if site.lower() not in websites_lower:
            logger.error(
                f"Error: '{site}' is not a valid website in the configuration. Valid options are: \n [{', '.join(websites)}]."
            )
            return
    for site in sites:
        run_one_website(site)

@log_function_call
def besins_run_multiple_websites():
    parser = argparse.ArgumentParser(
        description="Run multiple downloaders for the specified websites. \n"
                    "Usage: besins_run_multiple_websites [website1] [website2] [website3] ..."
    )
    parser.add_argument(
        "websites",
        nargs="+",
        help="Provide a list of websites to process. Each website should be specified by its name."
    )
    args = parser.parse_args()
    list_of_websites = args.websites 
    run_multiple_websites(list_of_websites)


@log_function_call
def launch_streamlit_app():
    def find_available_port(start_port: int) -> int:
        """Find an available port starting from start_port."""
        _port = start_port
        while True:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                try:
                    if s.connect_ex(("localhost", _port)) != 0:  # Port is available
                        return _port
                    else:
                        _port += 1
                except Exception as e:
                    logger.info(
                        f"An exception occurred while finding an available port: {e}"
                    )
                    _port += 1

    python_file_path = os.path.abspath(
        inspect.getfile(besins_selenium_downloaders.streamlit_app.app)
    )
    address = get_streamlit_server_address()
    port = str(get_streamlit_server_port())  # Ensure port is a string
    port = str(find_available_port(int(port)))  # Convert to string
    allow_run_on_save = str(get_streamlit_allow_run_on_save()).lower()

    command = [
        sys.executable,
        "-m",
        "streamlit",
        "run",
        python_file_path,
        "--server.address",
        address,
        "--server.port",
        port,
        "--server.allowRunOnSave",
        allow_run_on_save,
        "--client.toolbarMode",
        "minimal",
        "--browser.gatherUsageStats",
        "false",
    ]

    # Debugging: Print types of each command element
    command_types = [type(arg) for arg in command]
    logger.debug(f"Command types: {command_types}")

    # Ensure all command elements are strings
    command = [str(arg) for arg in command]

    # Debugging: Print the command list
    logger.debug(f"Command list: {command}")

    try:
        subprocess.run(command, check=True)
    except subprocess.CalledProcessError as e:
        logger.error(f"An error occurred while launching Streamlit app: {e}")
        sys.exit(1)
    except Exception as e:
        logger.error(f"An unexpected error occurred: {e}")
        logger.error(f"Command list: {command}")
        sys.exit(1)


@log_function_call
def besins_launch_streamlit_app():
    launch_streamlit_app()

@log_function_call
def run_sms_websites():
    pass
